package sketch.specs;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Type;

import sketch.generator.CompositeGenerator;
import sketch.generator.IGenerator;
import sketch.generator.IIterationLink;
import sketch.generator.LinkCreator;
import sketch.generator.experiment.MultiValue;
import sketch.generator.experiment.ValueGroup;
import sketch.generator.experiment.ValueGroupGenerator;
import sketch.util.Checker;

public class StepExhaustedGroupGenerator<T> {
	//public final Map<SimpleName,  ValueGroupGenerator<T>> nameGenerators;
//	public final Map<SimpleName, Type> typeMaps;
//	public final Collection<ValueGroupGenerator<T>> generators;
	
	//the inner data generator
	public final InnerExhaustiveGroupValueGenerator<T> real_generator;
	
	public StepExhaustedGroupGenerator(Map<SimpleName,  ValueGroupGenerator<T>> nameGeneratorMap, Map<SimpleName, Type> typeMaps) {
		Checker.checkTrue( typeMaps.keySet().containsAll(nameGeneratorMap.keySet()),//  nameGeneratorMap.keySet().equals(typeMaps.keySet()),
				"The keySet should contain nameGeneratorMap.key, however nameGeneratorMap keyset: "
				+ nameGeneratorMap.keySet() + ",  and typeMaps keyset: " + typeMaps.keySet());
		//this.nameGenerators = nameGeneratorMap;
//		this.typeMaps = typeMaps;
//		this.generators = nameGeneratorMap.values();
		real_generator = new InnerExhaustiveGroupValueGenerator<T>(nameGeneratorMap);
	}
	
	public boolean hasNext() {
		return this.real_generator.hasNext();
	}
	
	public void reset() {
		this.real_generator.reset();
	}
	
	public boolean isReset() {
		return this.real_generator.isReset();
	}
	
	//the value group contains either expression or list
	public ValueGroup next() {
		return this.real_generator.next();
	}
	
	public /*T*/ Object lookupCurrentValue(SimpleName name) {
		ValueGroup current_vg = this.real_generator.lookupCurrentValue(name);
		if(current_vg.isSingleData()) {
			return current_vg.getSingleData();
		} else if (current_vg.isMultiData()) {
			return current_vg.data;
		} else {
			throw new RuntimeException("Unexpected value group type: " + current_vg.getClass());
		}
	}
}

class InnerExhaustiveGroupValueGenerator<T> extends CompositeGenerator<ValueGroup> {
	
	public final Map<SimpleName,  ValueGroupGenerator<T>> nameGenerators;
	public final List<ValueGroupGenerator<T>> generators = new LinkedList<ValueGroupGenerator<T>>();
	
	public InnerExhaustiveGroupValueGenerator(Map<SimpleName,  ValueGroupGenerator<T>> nameGenerators) {
		this.nameGenerators = nameGenerators;
		this.generators.addAll(nameGenerators.values());
	}
	
	public ValueGroup lookupCurrentValue(SimpleName name) {
		return nameGenerators.get(name).current();
	}

	@Override
	public ValueGroup generateCurrent() {
		
		//a bit ugly, the list could only contain T, and List type
		List data = new LinkedList();
		
		for(ValueGroupGenerator<T> generator : generators) {
			//System.out.println(generator.getStep());
			ValueGroup<T> vg = generator.current();
			Checker.checkNull(vg, "The vg could not be null");
			Checker.checkTrue(!vg.isEmptyData(), "It should not be an empty data.");
			if(vg.isSingleData()) {
				data.add(vg.getSingleData());
			} else if (vg.isMultiData()) {
				data.add(vg.data);
			} else {
				throw new RuntimeException("unexptected data type: " + vg.getClass());
			}
		}
		
		ValueGroup ret_value_group = new MultiValue(data);
		
		return ret_value_group;
	}

	@Override
	public IIterationLink getInitializedIterator() {
		List<IGenerator<?>> generator_list = new LinkedList<IGenerator<?>>();
		for(ValueGroupGenerator<T> vg_generator : this.generators) {
			generator_list.add(vg_generator);
		}
//		return LinkCreator.createSequentialLink(generator_list);
		return LinkCreator.createCombinatoricLink((IGenerator<?>[])generator_list.toArray(new IGenerator<?>[0]));
	}
	
}